TOOL.Tab 		= "Fallen Menu"
TOOL.Category	= "Tech"
TOOL.Name        = "#Prop Breakable"
TOOL.Command     = nil
TOOL.ConfigName  = ""

// Tables

local Breakable = {}

// ConVars

TOOL.ClientConVar["Health"]   = "100"
TOOL.ClientConVar["Attached"] = "1"
TOOL.ClientConVar["Use"] = "1"

// Client

if (CLIENT) then
	language.Add("Tool_breakableprops_name", "Breakable Props Tool")
	language.Add("Tool_breakableprops_desc", "Make an entity breakable")
	language.Add("Tool_breakableprops_0", "Click on an entity to make it breakable and Right click to repair it!")
	
	language.Add("Undone_breakableprops", "Undone Breakable Prop")
end

// Message

function TOOL:Message(Text)
	if SERVER then
		self:GetOwner():SendLua("GAMEMODE:AddNotify('"..Text.."', NOTIFY_GENERIC, 10)")
		self:GetOwner():SendLua("surface.PlaySound('ambient/water/drip"..math.random(1, 4)..".wav')")
	end
end

// Get entities

local function ExtractEntities(Entity, Entities, Constraints)
	Constraints = Constraints or {}
	
	Entities = Entities or {}
	
	if (!Entity:IsValid()) then return Entities, Constraints end

	local Index = Entity:EntIndex()
	
	Entities[Index] = Entity
	
	if (!constraint.HasConstraints(Entity)) then return Entities, Constraints end
	
	local Cons = constraint.GetTable(Entity)
	
	for K, V in pairs(Cons) do
		local Index = V.Constraint
		
		if (!Constraints[Index]) then
			Constraints[Index] = V.Constraint
			
			for B, J in pairs(V.Entity) do
				local E, C = ExtractEntities(J.Entity, Entities, Constraints)
				
				table.Merge(Entities, E)
				table.Merge(Constraints, C)
			end
		end
	end
	
	return Entities, Constraints
end

// Left click

function TOOL:LeftClick(Trace)
	if (Trace.Entity) then
		if !Trace.Entity:IsValid() or Trace.Entity:IsPlayer() or Trace.HitWorld or Trace.Entity:IsNPC() then
			return false
		end
	end
	
	if(CLIENT) then
		return true
	end

	if(!SERVER) then return false end

	local HP       = Trace.Entity:BoundingRadius() * 3
	local Attached = self:GetClientNumber("Attached")
	
	HP = math.floor(HP)
	
	if self:GetClientNumber("Use") == 1 then
		HP = math.floor(self:GetClientNumber("Health"))
	end
	
	Trace.Entity.Breakable = {}
	
	Trace.Entity.Breakable.Cur = HP
	Trace.Entity.Breakable.Max = HP

	if Trace.Entity:GetTable().OnTakeDamage then
		local Prev = Trace.Entity:GetTable().OnTakeDamage
		
		function Trace.Entity:OnTakeDamage(Info)
			Breakable.DamagePhysicsEntity(self, Info:GetDamage())
			
			Prev(self, Info)
		end
	end
	
	if (Wire_CreateOutputs) then
		Trace.Entity.Outputs = Wire_CreateOutputs(Trace.Entity, {"Health", "Max"})
	end
	
	if (Wire_TriggerOutput) then
		Wire_TriggerOutput(Trace.Entity, "Health", HP)
		Wire_TriggerOutput(Trace.Entity, "Max", HP)
	end
	
	local Undos = {}
	
	table.insert(Undos, Trace.Entity)
	
	if (Attached == 1) then
		local Entities, Constraints = ExtractEntities(Trace.Entity)
		
		for K, V in pairs(Entities) do
			if V:IsValid() then
				if (V != Trace.Entity) then
					local HP = V:BoundingRadius() * 3
					
					if self:GetClientNumber("Use") == 1 then
						HP = math.floor(self:GetClientNumber("Health"))
					end
					
					V.Breakable = {}
					
					V.Breakable.Cur = HP
					V.Breakable.Max = HP
					
					if (Wire_CreateOutputs) then
						V.Breakable.Outputs = Wire_CreateOutputs(V, {"Health", "Max"})
					end
					
					if (Wire_TriggerOutput) then
						Wire_TriggerOutput(V, "Health", HP)
						Wire_TriggerOutput(V, "Max", HP)
					end
					
					if (V:GetTable().OnTakeDamage) then
						local Prev = V:GetTable().OnTakeDamage
						
						function V:OnTakeDamage(Info)
							Breakable.DamagePhysicsEntity(self, Info:GetDamage())
							
							Prev(self, Info)
						end
					end
					
					table.insert(Undos, V)
				end
			end
		end
	end
	
	if self:GetClientNumber("Use") != 1 then
		self:Message("Entities health will be 3 times the objects bounding radius!")
	else
		local HP = math.floor(self:GetClientNumber("Health"))
		
		self:Message("Entities health are set to "..HP)
	end
	
	// Undo
	
	local function Function(Undo, Table)
		for K, V in pairs(Table) do
			if (V:IsValid()) then
				Breakable.Extinguish(V)
				
				V.Breakable = nil
			end
		end
	end
	
	undo.Create("breakableprops")
		undo.SetPlayer(self:GetOwner())
		undo.AddFunction(Function, Undos)
	undo.Finish()
	
	return true
end

// Right cick

function TOOL:RightClick(Trace)
	if (Trace.Entity) then
		if !Trace.Entity:IsValid() or Trace.Entity:IsPlayer() or Trace.HitWorld or Trace.Entity:IsNPC() then
			return false
		end
	end
	
	if(CLIENT) then
		if (Trace.Entity) then
			local Effect = EffectData()
				Effect:SetEntity(Trace.Entity)
				Effect:SetOrigin(Trace.Entity:GetPos())
				Effect:SetStart(Trace.Entity:GetPos())
				Effect:SetScale(999)
				Effect:SetMagnitude(250)
			util.Effect("WaterSplash", Effect)
		end
		
		return true
	end

	if(!SERVER) then return false end

	local Entities, Constraints = ExtractEntities(Trace.Entity)
	
	for K, V in pairs(Entities) do
		if (V.Breakable and V.Breakable.Max != 0) then
			Breakable.Extinguish(V)
		end
	end
	
	self:Message("This prop and connected props have been restored to full health!")
	
	return true
end

if SERVER then
	// Extinguish
	
	function Breakable.Extinguish(Entity)
		local HP = Entity.Breakable.Max
		
		Entity.Breakable.Cur = HP
		
		timer.Remove("Destroy:"..Entity:EntIndex())
		
		if (Entity:IsOnFire()) then
			Entity:Extinguish()
		end
		
		Breakable.DeleteEntityTrail(Entity)
		
		Entity:SetColor(255, 255, 255, 255)
		
		if (Wire_TriggerOutput) then
			Wire_TriggerOutput(Entity, "Health", HP)
			Wire_TriggerOutput(Entity, "Max", HP)
		end
	end
	
	// Delete entity trail
	
	function Breakable.DeleteEntityTrail(Entity)
		if (Entity.Breakable.Smoke and Entity.Breakable.Smoke:IsValid()) then
			Entity.Breakable.Smoke:Remove()
			
			Entity.Breakable.Smoke = nil
		end
	end
	
	// Set health based on color
	function Breakable.ColorHealthBased(Entity, HP, Col)
		if (Entity.Breakable.Cur <= (Entity.Breakable.Max / HP)) then
			Entity:SetColor(Col, Col, Col, 255)
		end
	end
	
	// Damage a physics entity
	
	function Breakable.DamagePhysicsEntity(Entity, Amount)
		Amount = math.floor(Amount / 2)
		
		if (Entity.Breakable and Entity.Breakable.Cur > 0) then
			local HP = Entity.Breakable.Cur - Amount
			
			Entity.Breakable.Cur = HP
			
			if (Entity.Breakable.Cur <= (Entity.Breakable.Max / 2)) then
				Breakable.CreateEntitySmokeTrail(Entity)
			end
			
			Breakable.ColorHealthBased(Entity, 2, 200)
			Breakable.ColorHealthBased(Entity, 3, 175)
			Breakable.ColorHealthBased(Entity, 4, 150)
			Breakable.ColorHealthBased(Entity, 5, 125)
			Breakable.ColorHealthBased(Entity, 6, 100)
			Breakable.ColorHealthBased(Entity, 7, 75)
			
			if (Entity.Breakable.Cur <= 0) then
				local function Func(Entity)
					if (Entity:IsValid()) then
						local Effect = EffectData()
							Effect:SetOrigin(Entity:GetPos())
							Effect:SetScale(1)
							Effect:SetMagnitude(25)
						util.Effect("Explosion", Effect, true, true)
						
						Entity:Remove()
					end
				end
				
				Entity:SetColor(50, 50, 50, 255)
				
				if not (Entity:IsOnFire()) then
					Entity:Ignite(30, 0)
				end
				
				timer.Create("Destroy:"..Entity:EntIndex(), 5, 1, Func, Entity)
			end
			
			if (Wire_TriggerOutput) then
				Wire_TriggerOutput(Entity, "Health", Entity.Breakable.Cur)
				Wire_TriggerOutput(Entity, "Max", Entity.Breakable.Max)
			end
		end
	end
	
	// Create smoke trail
	
	function Breakable.CreateEntitySmokeTrail(Entity)
		if not (Entity.Breakable.Smoke) then
			local Smoke = ents.Create("env_smoketrail")
			
			Smoke:SetKeyValue("opacity", 1)
			Smoke:SetKeyValue("spawnrate", 25)
			Smoke:SetKeyValue("lifetime", 2)
			Smoke:SetKeyValue("startcolor", "100 100 100")
			Smoke:SetKeyValue("endcolor", "125 125 125")
			Smoke:SetKeyValue("minspeed", 15)
			Smoke:SetKeyValue("maxspeed", 30)
			Smoke:SetKeyValue("startsize", (Entity:BoundingRadius() / 2))
			Smoke:SetKeyValue("endsize", Entity:BoundingRadius())
			Smoke:SetKeyValue("spawnradius", 10)
			Smoke:SetKeyValue("emittime", 300)
			Smoke:SetKeyValue("firesprite", "sprites/firetrail.spr")
			Smoke:SetKeyValue("smokesprite", "sprites/whitepuff.spr")
			Smoke:SetPos(Entity:GetPos())
			Smoke:SetParent(Entity)
			Smoke:Spawn()
			Smoke:Activate()
			
			// Delete on remove
			
			Entity:DeleteOnRemove(Smoke)
			
			// Networked variables
			
			Entity.Breakable.Smoke = Smoke
		end
	end
	
	// Entity take damage
	
	function TOOL.EntityTakeDamage(Entity, One, Two, Damage)
		Breakable.DamagePhysicsEntity(Entity, Damage)
	end
	
	hook.Add("EntityTakeDamage", "TOOL.EntityTakeDamage", TOOL.EntityTakeDamage)
end

// Build CPanel

function TOOL.BuildCPanel(Panel)
	Panel:AddControl("Header", {Text = "#Tool_breakableprops_name", Description	= "#Tool_breakableprops_desc"})

	Panel:AddControl("Checkbox", {Label = "#Make attached props breakable", Command = "breakableprops_Attached"})
	
	Panel:AddControl("Checkbox", {Label = "#Use specified health", Command = "breakableprops_Use"})

	Panel:AddControl("Slider", {Label = "#Health", Type	= "Integer", Min = 0, Max = 10000, Command = "breakableprops_Health"})
end